<?php

namespace App\Services;

use App\Exceptions\LogicException;
use App\Models\Api\Apis;
use App\Models\Api\ApisParam;
use App\Models\Api\ApisValidator;
use App\Models\Env\Host;
use App\Models\Env\HostEnv;
use App\Models\Module\ProductModule;
use App\Models\Projects\Product;
use App\Models\Projects\Project;
use App\Models\Projects\ProjectCate;
use App\Models\TestApi\TestApi;
use App\Models\TestApi\TestModule;
use App\Models\TestApi\TestParamRes;
use App\Models\TestApi\TestReport;
use App\Http\Controllers\Admin\RunTest\RunTestController;
use App\Models\TestApi\TestValidatorRes;

class makeReportService
{
    /**
     * ---测试单
     * @var TestReport
     * @type object
     */
    public TestReport $testReport;

    /**
     * ---产品
     * @var Product
     * @type object
     */
    public Product $product;

    public int $product_id = 0;
    public array $f_module_ids = [];
    public array $s_module_ids = [];
    public array $api_ids = [];
    public string $cate_name = '';
    public string $project_name = '';
    public array $report_explain = [];

    public function initData(?TestReport $testReport,int $id_type, int $product_id = 0, array $f_module_ids = [], array $s_module_ids = [])
    {
        if(!$testReport) {
            throw new LogicException('请提前生成测试单');
        }

        if($id_type === RunTestController::ID_TYPE_NULL) {
            throw new LogicException('生成测试单失败,请选择有效数据');
        }
        $this->id_type = $id_type;
        $this->testReport = $testReport;
        $this->product_id = $product_id;
        $this->f_module_ids = $f_module_ids;
        $this->s_module_ids = $s_module_ids;

        if($id_type === RunTestController::ID_TYPE_PRODUCT) {
            $this->checkProduct($this->product_id);
            return $this;
        }

        if($id_type === RunTestController::IDS_TYPE_FMODULE) {
            $this->checkFmodules($this->f_module_ids);
            return $this;
        }

        if($id_type === RunTestController::IDS_TYPE_SMODULE) {
            $this->checkSmodules($this->s_module_ids);
            return $this;
        }

        return $this;
    }

    public function checkProduct(int $product_id)
    {
        $this->product = Product::find($product_id);
        if(!$this->product) {
            throw new LogicException('产品不存在');
        }
        $this->cate_name = ProjectCate::find($this->product->cate_id)->name ?? '';
        $this->project_name = Project::find($this->product->project_id)->name ?? '';
        if($this->product->enable !== 1) {
            throw new LogicException('产品在下架状态,不能生成测试单');
        }

        $this->f_module_ids = ProductModule::getFidsByProductId($this->product->id);
        if(empty($this->f_module_ids)) {
            throw new LogicException('产品下没有上架的主模块,不能生成测试单');
        }

        $this->s_module_ids = ProductModule::getFidsByFids($this->f_module_ids);
        if(empty($this->s_module_ids)) {
            throw new LogicException('产品下没有上架的子主模块,不能生成测试单');
        }
        return $this;
    }

    public function checkFmodules(array $f_module_ids)
    {
        if(empty($f_module_ids)) {
            throw new LogicException('请选择要测试的主模块');
        }

        $f_module_one = ProductModule::find($f_module_ids[0]);
        if(!$f_module_one) {
            throw new LogicException('主模块不存在');
        }

        $this->product = Product::find($f_module_one->product_id);
        if(!$this->product) {
            throw new LogicException('产品不存在');
        }

        if($this->product->enable !== 1) {
            throw new LogicException('产品在下架状态,不能生成测试单');
        }

        // 判断是不是同一产品下的模块
        $product_sum = ProductModule::select('product_id')->distinct('product_id')->whereIn('id', $f_module_ids)->get()->toArray();
        if(count($f_module_ids) !==1 && $product_sum !== 1) {
            throw new LogicException('请选择同一产品下的主模块生成测试单');
        }

        $this->cate_name = ProjectCate::find($this->product->cate_id)->name ?? '';
        $this->project_name = Project::find($this->product->project_id)->name ?? '';
        $this->f_module_ids = ProductModule::getFidsByFIds2($f_module_ids);
        if(empty($this->f_module_ids)) {
            throw new LogicException('产品下没有上架的主模块,不能生成测试单');
        }

        $this->s_module_ids = ProductModule::getFidsByFids($this->f_module_ids);
        if(empty($this->s_module_ids)) {
            throw new LogicException('产品下没有上架的子主模块,不能生成测试单');
        }

        return $this;
    }

    public function checkSmodules(array $s_module_ids)
    {
        if(empty($s_module_ids)) {
            throw new LogicException('请选择要测试的子模块');
        }

        $s_module_one = ProductModule::find($s_module_ids[0]);
        if(!$s_module_one) {
            throw new LogicException('子模块不存在');
        }

        $this->product = Product::find($s_module_one->product_id);
        if(!$this->product) {
            throw new LogicException('产品不存在');
        }

        if($this->product->enable !== 1) {
            throw new LogicException('产品在下架状态,不能生成测试单');
        }

        $this->cate_name = ProjectCate::find($this->product->cate_id)->name ?? '';
        $this->project_name = Project::find($this->product->project_id)->name ?? '';

        // 判断是不是同一产品下的模块
        $product_sum = ProductModule::select('product_id')->distinct('product_id')->whereIn('id', $s_module_ids)->get()->toArray();
        if(count($s_module_ids) !== 1 && count($product_sum) !== 1) {
            throw new LogicException('请选择同一产品下的子模块生成测试单');
        }

        $this->f_module_ids = ProductModule::getFidsBySIds($s_module_ids);
        if(empty($this->f_module_ids)) {
            throw new LogicException('产品下没有上架的主模块,不能生成测试单');
        }

        $this->s_module_ids = ProductModule::getSidsBySids($s_module_ids);
        if(empty($this->s_module_ids)) {
            throw new LogicException('产品下没有上架的子主模块,不能生成测试单');
        }

        return $this;
    }

    public function makeTestModules()
    {
        foreach($this->s_module_ids as $sid) {
            $smodule = ProductModule::find($sid);
            if(!$smodule) {
                throw new LogicException('子模块不存在,不能生成测试单');
            }
            $fmodule = ProductModule::find($smodule->p_id);
            if(!$fmodule) {
                throw new LogicException('主模块不存在,不能生成测试单');
            }

            $test_api_ids = Apis::query()->where('s_module_id', $smodule->id)->where('enable', 1)->pluck('id');
            if(empty($test_api_ids)) {
                continue;
            }

            $test_param_sun = ApisParam::whereIn('api_id', $test_api_ids)->count();
            if($test_param_sun == 0) {
                continue;
            }
            $TestModule = TestModule::create([
                'test_report_uid' => $this->testReport->test_report_uid,
                'cate_name' => $this->cate_name,
                'project_name' => $this->project_name,
                'product_name' => $this->product->name,
                'f_module_name' => $fmodule->name,
                's_module_name' => $smodule->name,
                's_module_id' => $smodule->id,
                'test_api_sun' => count($test_api_ids),
                'test_param_sun' => $test_param_sun,
                'success_param_sun' => 0,
                'status' => TestModule::STATUS_INIT
            ]);

            $this->report_explain[] = $TestModule->cate_name.'->'.
                $TestModule->project_name.'->'.
                $TestModule->product_name.'->'.
                $TestModule->f_module_name.'->'.
                $TestModule->s_module_name;
        }
        return $this;
    }

    public function makeTestApis()
    {
        $this->api_ids = Apis::getApiIdsBySids($this->s_module_ids);
        foreach($this->api_ids as $api_id) {
            $api = Apis::find($api_id);
            if(!$api) {
                continue;
            }
            $smodule = ProductModule::find($api->s_module_id);
            if(!$smodule) {
                continue;
            }
            $test_module = TestModule::where('test_report_uid', $this->testReport->test_report_uid)->where('s_module_id', $smodule->id)->first();
            if(!$test_module) {
                continue;
            }
            $test_param_sun = ApisParam::where('api_id', $api->id)->count();
            if($test_param_sun == 0) {
                $test_module->test_api_sun -= 1;
                $test_module->save(); // 不测试没有参数的接口
                $this->testReport->test_api_sun -= 1; // 不测试没有参数的接口
                $this->testReport->save();
                continue;
            }
            $headers = Apis::getHeaders($api) ?: [];
            $method_type = Apis::$method_type_maps[$api->method_type];

            $api_info = $this->getUrl($api->api);

            $domain = $api_info['scheme']."://".$api_info['domain'];
            if(!empty($api_info['port'])) {
                $domain = $api_info['scheme']."://".$api_info['domain'] .':'.$api_info['port'];
            }
            $TestApi = TestApi::create([
                'test_report_uid' => $this->testReport->test_report_uid,
                'test_module_id' => $test_module->id,
                'api_id' => $api->id,
                'api_name' => $api->name,
                'api' => $api->api,
                'path' => $api_info['path'],
                'domain' => $domain,
                'headers' => $headers,
                'method_type' => $method_type,
                'test_param_sun' => $test_param_sun,
                'success_param_sun' => 0,
                'status' => TestApi::STATUS_INIT
            ]);

            // todo 添加参数 验证器
            $params = ApisParam::where('api_id', $api->id)->orderBy('id', "ASC")->get();
            foreach ($params as $param) {
                $test_validator_sum = ApisValidator::where("param_id", $param->id)->count();
                $TestParamRes = TestParamRes::create([
                    'test_report_uid' => $this->testReport->test_report_uid,
                    'test_module_id' => $TestApi->test_module_id,
                    'test_api_id' => $TestApi->id,
                    'api_id' => $api->id,
                    'param_id' => $param->id,
                    'param' => $param->param,
                    'res' => [],
                    'http_code' => 0,
                    'duration' => 0,
                    'test_validator_sum' => $test_validator_sum,
                    'success_validator_sum' => 0,
                    'status' => TestParamRes::STATUS_INIT
                ]);

                if($test_validator_sum == 0) {
                    continue; // 没有验证器的参数不用添加
                }

                $validators = ApisValidator::where("param_id", $TestParamRes->param_id)->orderBy('id', "ASC")->get();
                foreach($validators as $validator) {
                    TestValidatorRes::create([
                        'test_report_uid' =>  $this->testReport->test_report_uid,
                        'test_module_id' => $TestParamRes->test_module_id,
                        'test_api_id' => $TestParamRes->test_api_id,
                        'api_id' => $TestParamRes->api_id,
                        'param_id' => $TestParamRes->param_id,
                        'validator_id' => $validator->id,
                        'param_res_id' => $TestParamRes->id,
                        'rule' => $validator->key,
                        'expect_value' => $validator->validator_value,
                        'true_value' => '',
                        'status' => TestValidatorRes::STATUS_INIT
                    ]);
                }
            }
        }
        return $this;
    }

    public function makeTestParams()
    {
        return $this;
    }

    public function makeTestvalidators()
    {
        return $this;
    }

    public function done()
    {

        if($is = TestModule::where('test_api_sun', '<=', 0)->where('test_report_uid', $this->testReport->test_report_uid)->first()){
            TestModule::where('test_api_sun', '<=', 0)->where('test_report_uid', $this->testReport->test_report_uid)->delete();
        }

        $this->testReport->explain = implode(',', $this->report_explain);
        $this->testReport->test_api_sun = TestModule::where('test_report_uid', $this->testReport->test_report_uid)->sum('test_api_sun');
        $this->testReport->test_param_sun = TestParamRes::where('test_report_uid', $this->testReport->test_report_uid)->count();
        $this->testReport->save();

        if($es = TestReport::where('test_api_sun', '<=', 0)->where('test_report_uid', $this->testReport->test_report_uid)->first()) {
            $es->delete();
            throw new LogicException('测试单下的接口没有可用的有效用例接口,生成测试单失败');
        }
        return $this;
    }

    public function getUrl($url): array
    {
        if(strpos("_".$url,'http') == true) {
            $tempu = parse_url($url);
            $path = $tempu['path'] ?? '';
            $domain = $tempu['host'] ?? '';
            $scheme = $tempu['scheme'] ?? '';
            $port = $tempu['port'] ?? '';
        } else {
            $host = explode("}}", substr($url,2,strlen($url)));
            if(empty($host[0])) {
                throw new LogicException('请用{{xl_admin_utl}}或者https或者http方式来补充接口域名');
            } else {
                $ex = HostEnv::getByName($host[0]);
                if(!$ex) {
                    throw new LogicException("{$host[0]}环境标识不存在,请添加环境标识");
                }
                if(!$host_url = Host::where('host_env_id', $ex->id)->where('enable', 1)->first()) {
                    throw new LogicException("{$host[0]}环境标识下没有可用的域名,请添加");
                }
            }
            $path = $host[1];
            $tempu = parse_url($host_url->http);
            $domain = $tempu['host'] ?? '';
            $scheme = $tempu['scheme'] ?? '';
            $port = $tempu['port'] ?? '';
        }
        return [
             'path' => $path,
             'domain' => $domain,
             'scheme' => $scheme,
             'port' => $port
        ];
    }
}
